<?php

/**
 * tickee Class
 *
 * This class is used to communicate with tickee (http://www.tick.ee)
 *
 * The class is documented in the file itself. Additional documentation can be found at http://developer.tick.ee , where you can also report bugs or feature requests.
 * If you report a bug, make sure you give us enough information (include your code).
 *
 *
 * Changelog since 0.1
 * - Thrown out all method-based functions. Now you simply call the REST function ( GET/POST/PUT/DELETE )
 * - Implemented basic cURL response code validation on 2xx
 *
 * Changelog since 0
 * - Changed to REST Wrapper.
 *
 * License
 * Copyright (c) 2011, tickee. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain this information header, line by line (currently from 3 untill 36).
 * 2. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission.
 *
 * This software is provided by the author "as is". In no event shall the author be liable for any kind of data loss, harm or damages. None.
 *
 * @author		Koen Betsens <we@tick.ee>
 * @version		0.1
 *
 * @copyright	Copyright (c) 2011, tickee. All rights reserved.
 * @license		BSD License
 */
 
class Tickee	{

	/**
	 * internal constant to enable/disable debugging
	 *
	 * @const	DEBUG
	 */
	const DEBUG = false;


	/**
	 * url, version & port for the tickee-api
	 *
	 * @const	API_URL
	 * @const	API_VERSION
	 * @const	API_PORT
	 */
	const API_URL = 'http://api.tick.ee/';
	const API_VERSION = "0.2";
	const API_PORT = 80;		#443 ssh

	
	/**
	 * Current library version
	 *
	 * @const	VERSION
	 */
	const VERSION = '0.2';


	/**
	 * A cURL instance
	 *
	 * @var	resource
	 */
	private $curl;


	/**
	 * The account key
	 *
	 * @var	string
	 */
	private $accountKey;


	/**
	 * The account secret
	 *
	 * @var	string
	 */
	private $accountSecret;
	
	/**
	 * The session scope
	 *
	 * @var	string
	 */
	private $accountScope = "scope_account_mgmt";


	/**
	 * The oAuth2-token
	 *
	 * @var	string
	 */
	private $accountToken = '';


	/**
	 * The session key
	 *
	 * @var	string
	 */
	private $sessionName = 'tickee_token';
	
	/**
	 * The session timer
	 *
	 * @var	string
	 */
	private $sessionTimer = 'tickee_timer';


	/**
	 * The timeout in seconds
	 *
	 * @var	int
	 */
	private $timeout = 3600;


#	class methods

	/**
	 * Default constructor
	 *
	 * @return	void
	 * @param	string $accountKey		The account key to use.
	 * @param	string $accountSecret	The account secret to use.
	 */
	public function __construct($accountKey, $accountSecret, $scope = false )
	{
#		set account key/secret pair
		$this->setAccountPair( $accountKey, $accountSecret );
		
#		set account scope
		if( $scope ) $this->accountScope = $scope; 
		
#		set cURL session
		$this->curl = curl_init();

#		Authenticate.
		$this->accountToken = $this->token();

	}


	/**
	 * Default destructor
	 *
	 * @return	void
	 */
	public function __destruct()
	{
		if( $this->curl ) curl_close( $this->curl );
	}



	/**
	 * Check cached authentication vs authentication request
	 *
	 * @return	array
	 */
	private function token() {

#				Is there Cache?
		if( !(	$key = $_SESSION[ $this->sessionName ])
		||	!	$_SESSION[ $this->sessionTimer ] || time() - $_SESSION[ $this->sessionTimer ] > $this->timeout ) {
		
#				Retreive key
				$key = $this->authenticate();
				$_SESSION[ $this->sessionTimer ] = time();
		}
		
		return	$key;
	}



	/**
	 * cURL authentication request
	 *
	 * @return	string		Session token
	 */
	private function authenticate() {

#		set auth request options
		curl_setopt_array( $this->curl, array(
			
			CURLOPT_URL				=> self::API_URL . "oauth2/token" . "?grant_type=client_credentials&scope=" . $this->accountScope,
			CURLOPT_PORT			=> self::API_PORT,
			CURLOPT_TIMEOUT 		=> (int) $this->timeout,
			CURLOPT_RETURNTRANSFER	=> true,
			CURLOPT_HTTPHEADER		=> array( "Authorization: Basic " . base64_encode( implode( ":", $this->getAccountPair() )) )
		
		));

#		exeute cURL
		$response	= curl_exec( $this->curl );
		$headers	= curl_getinfo( $this->curl );

#		fetch errors
		$this->validateResponse( $headers['http_code'], $response );
		
#		store and return token		
		return
			$_SESSION[ $this->sessionName ] = json_decode( $response )->access_token;

	}



	/**
	 * cURL GET request
	 *
	 * @return	string					The cURL response
	 *
	 * @param	string	$resource		The API base resource uri
	 * @param	mixed	$id				Optional ID('s)
	 * @param	array	$params			Optional array with parameters
	 */
	public function get( $resource, $id, $params = null ) {

#		create request URL
		$url = $this->getURL( $resource, $id, $params );

#		set auth request options
		curl_setopt_array( $this->curl, array(

			CURLOPT_URL				=> $url,
			CURLOPT_PORT			=> self::API_PORT,
			CURLOPT_TIMEOUT 		=> (int) $this->timeout,
			CURLOPT_RETURNTRANSFER	=> true,
			CURLOPT_HTTPHEADER		=> array( "Authorization: Bearer " . $this->token())

		));

#		exeute cURL
		$response	= curl_exec( $this->curl );
		$headers	= curl_getinfo( $this->curl );

#		fetch errors
		$this->validateResponse( $headers['http_code'], $response );
		
#		if 2xx,  return response		
		return $response;

	}
	


	/**
	 * cURL POST request
	 *
	 * @return	string					The cURL response
	 *
	 * @param	string	$resource		The API base resource uri
	 * @param	mixed	$id				Optional ID('s)
	 * @param	array	$params			Optional array with parameters
	 */
	public function post( $resource, $id, $params = null ) {

#		create request URL
		$url = $this->postURL( $resource, $id );

#		set auth request options
		curl_setopt_array( $this->curl, array(

			CURLOPT_URL				=> $url,
			CURLOPT_PORT			=> self::API_PORT,
			CURLOPT_TIMEOUT 		=> (int) $this->timeout,
			CURLOPT_RETURNTRANSFER	=> true,
			CURLOPT_POST			=> true,
			CURLOPT_POSTFIELDS		=> $this->buildQuery( $params ),
			CURLOPT_HTTPHEADER		=> array( "Authorization: Bearer " . $this->token()) //"Content-Type: application/json; charset=utf-8"

		));

#		exeute cURL
		$response	= curl_exec( $this->curl );
		$headers	= curl_getinfo( $this->curl );

#		fetch errors
		$this->validateResponse( $headers['http_code'], $response );
		
#		if 2xx,  return response		
		return $response;

	}
	
	
	
	/**
	 * cURL PUT request
	 *
	 * @return	string					The cURL response
	 *
	 * @param	string	$resource		The API base resource uri
	 * @param	mixed	$id				Optional ID('s)
	 * @param	array	$params			Optional array with parameters
	 */
	public function put( $resource, $id, $params = null ) {

#		create request URL
		$url = $this->postURL( $resource, $id );

#		set auth request options
		curl_setopt_array( $this->curl, array(

			CURLOPT_URL				=> $url,
			CURLOPT_PORT			=> self::API_PORT,
			CURLOPT_TIMEOUT 		=> (int) $this->timeout,
			CURLOPT_RETURNTRANSFER	=> true,
			CURLOPT_CUSTOMREQUEST	=> "PUT",
			CURLOPT_POSTFIELDS		=> $this->buildQuery( $params ),
			CURLOPT_HTTPHEADER		=> array( "Authorization: Bearer " . $this->token()) //"Content-Type: application/json; charset=utf-8"

		));

#		exeute cURL
		$response	= curl_exec( $this->curl );
		$headers	= curl_getinfo( $this->curl );

#		fetch errors
		$this->validateResponse( $headers['http_code'], $response );
		
#		if 2xx,  return response		
		return $response;

	}



	/**
	 * cURL DELETE request
	 *
	 * @return	string					The cURL response
	 *
	 * @param	string	$resource		The API base resource uri
	 * @param	mixed	$id				Optional ID('s)
	 * @param	array	$params			Optional array with parameters
	 */
	public function delete( $resource, $id, $params = null ) {

#		create request URL
		$url = $this->postURL( $resource, $id );

#		set auth request options
		curl_setopt_array( $this->curl, array(

			CURLOPT_URL				=> $url,
			CURLOPT_PORT			=> self::API_PORT,
			CURLOPT_TIMEOUT 		=> (int) $this->timeout,
			CURLOPT_RETURNTRANSFER	=> true,
			CURLOPT_CUSTOMREQUEST	=> "DELETE",
			CURLOPT_POSTFIELDS		=> $this->buildQuery( $params ),
			CURLOPT_HTTPHEADER		=> array( "Authorization: Bearer " . $this->token()) //"Content-Type: application/json; charset=utf-8"

		));

#		exeute cURL
		$response	= curl_exec( $this->curl );
		$headers	= curl_getinfo( $this->curl );

#		fetch errors
		$this->validateResponse( $headers['http_code'], $response );
		
#		if 2xx,  return response		
		return $response;

	}



	/**
	 * Format the parameters as a querystring
	 *
	 * @return	string
	 * @param	array $parameters	The parameters.
	 */
	private function buildQuery( array $parameters )	{

#		no parameters?
		if( empty( $parameters )) return '';

#		process parameters, rfc3986 encode value
		foreach( $parameters as $key => $value ) $parameters[ $key ] = $key . '=' . rawurlencode( $value );
		
#		combine and return query
		return 	implode( "&", $parameters );

	}



	/**
	 * parse API URL for GET use
	 *
	 * @return	array
	 * @param	string $module		The method module.
	 */
	private function getURL( $resource, $id, $params = false ) {

#		add 
		if( $id && !strpos( $resource, '%' ))
				$resource .= "$id";
		
		else if( is_array( $id )) {
			 if( count( $id ) > substr_count( $resource, '%' )) $resource .= '%' . count( $id );

			 	$resource = preg_replace('/\%/', $id, $resource );
		} else	$resource = str_replace( '%', $id, $resource ); 
		
#		Url encode optional parameters		
		if( $params )
			$resource .= "?" . $this->buildQuery( $params );
		
#		return united string
		return self::API_URL . self::API_VERSION . $resource;
	}



	/**
	 * parse API URL for POST use
	 *
	 * @return	array
	 * @param	string $module		The method module.
	 */
	private function postURL( $resource, $id ) {
		
#		return united string
		if( $id && !strpos( $resource, '%' ))
				$resource .= "$id";
		
		else if( is_array( $id )) {
			 if( count( $id ) > substr_count( $resource, '%' )) $resource .= '%' . count( $id );

			 	$resource = preg_replace('/\%/', $id, $resource );
		} else	$resource = str_replace( '%', $id, $resource ); 
		
#		return united string
		return self::API_URL . self::API_VERSION . $resource;		

	}



	/**
	 * Get the account key and secret
	 *
	 * @return	array	the account key and secret
	 */
	private function getAccountPair()	{

		return array( $this->accountKey, $this->accountSecret );
	}



	/**
	 * Set the account key and secret
	 *
	 * @return	void
	 * @param	string $key		The account key to use.
	 * @param	string $secret	The account secret to use.
	 */
	private function setAccountPair( $key, $secret )	{

		$this->accountKey		= (string) $key;
		$this->accountSecret	= (string) $secret;
	}



	/**
	 * Check cURL response header (status code)
	 *
	 * @return	void
	 * @param	string $code		The cURL response status code.
	 * @param	string $response	The response body.
	 */
	private function validateResponse( $code, $response )	{

		if( strpos( $code, '2' ) === 0 )
			return true;
		
		preg_match( '/<title>([^<]*)/', $response, $error );
		throw new TickeeException( $error[1], $code );
		
	}
}


/**
 * Tickee Exception class
 *
 * @author	Koen Betsems <we@tick.ee>
 */
class TickeeException extends Exception
{
}

?>